package com.hirsi.common.core.handler;

import cn.hutool.core.collection.CollUtil;
import com.hirsi.common.core.enums.IVoDataValidEnum;
import com.hirsi.common.core.exception.VoDataValidError;
import com.hirsi.common.core.util.HttpContextUtil;
import com.hirsi.common.core.exception.ServerRuntimeException;
import com.hirsi.common.core.vo.result.FieldValidResult;
import com.hirsi.common.core.vo.result.ResponseResult;
import io.lettuce.core.KeyValue;
import java.lang.reflect.Field;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 全局异常处理类
 */
@Slf4j
@RestControllerAdvice
public class RestControllerExceptionHandler {


    //自定义错误
    @ExceptionHandler(ServerRuntimeException.class)
    public ResponseResult<Object> helloException(ServerRuntimeException e) {
        HttpContextUtil.getResponse().setStatus(e.getStatus().value());
        log.warn("【警告异常】", e);
        return ResponseResult.fail(e);
    }

    //数据库异常
    @ExceptionHandler(MyBatisSystemException.class)
    public ResponseResult<Object> myBatisSystemException(MyBatisSystemException e) {
        //验证字段值已存在
        if (e.getCause().getCause() instanceof ServerRuntimeException) {
            ServerRuntimeException cause = (ServerRuntimeException) e.getCause().getCause();
            HttpContextUtil.getResponse().setStatus(cause.getStatus().value());
            return ResponseResult.fail(cause);
        }
        log.error(String.format("【系统错误】, 接口：%s", HttpContextUtil.getUrl()), e);
        return ResponseResult.fail();
    }

    @ExceptionHandler({BindException.class, MethodArgumentNotValidException.class, ConstraintViolationException.class})
    public ResponseResult<Object> bingException(Exception e) {
        List<FieldError> fieldErrors = new ArrayList<>();
        List<String> fieldNames = new ArrayList<>();
        if (e instanceof BindException) {
            fieldErrors = ((BindException) e).getBindingResult().getFieldErrors();
            fieldNames = Arrays.asList(((BindException) e).getBindingResult().getTarget().getClass().getDeclaredFields()).stream().map(Field::getName).collect(Collectors.toList());
        } else if (e instanceof MethodArgumentNotValidException) {
            fieldErrors = ((MethodArgumentNotValidException) e).getBindingResult().getFieldErrors();
            fieldNames = Arrays.asList(((MethodArgumentNotValidException) e).getBindingResult().getTarget().getClass().getDeclaredFields()).stream().map(Field::getName).collect(Collectors.toList());
        }
        if (CollUtil.isNotEmpty(fieldErrors)) {
            //入参字段验证
            List<IVoDataValidEnum> fieldValids = fieldErrors.stream().map(item -> new FieldValidResult(item.getField(), item.getDefaultMessage())).collect(Collectors.toList());
            List<String> finalFieldNames = fieldNames;
            fieldValids.sort(Comparator.comparingInt(o -> finalFieldNames.indexOf(o.getField())));
            VoDataValidError exception = new VoDataValidError(fieldValids.get(0).getMsg(), fieldValids);
            return ResponseResult.fail(new ServerRuntimeException(exception));
        }
        log.error(String.format("【系统错误】, 接口：%s", HttpContextUtil.getUrl()), e);
        return ResponseResult.fail();
    }

    //json参数错误的问题
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseResult<Void> httpMessageNotReadableException(HttpMessageNotReadableException e) {
        log.error(String.format("【系统错误】, 接口：%s", HttpContextUtil.getUrl()), e);
        return ResponseResult.fail();
    }

    //其他异常
    @ExceptionHandler(Exception.class)
    public ResponseResult<Void> exception(Exception e) {
        log.error(String.format("【系统错误】, 接口：%s", HttpContextUtil.getUrl()), e);
        return ResponseResult.fail();
    }
}
